<?php


namespace ioc\src;

/**
 * Class Ioc
 * @package ioc\src
 * @title 相当于一生二，二生三，三生万物
 */
class Ioc
{
    public $binding = [];

    /**
     * @param $abstract string 抽象类
     * @param $concrete string 具体实例化的类
     */
    public function bind($abstract, $concrete)
    {
        //这里为什么要返回一个closure呢？因为bind的时候还不需要创建User对象，所以采用closure等make的时候再创建FileLog;
        $this->binding[$abstract]['concrete'] = function (Ioc $ioc) use ($concrete) {
            return $ioc->build($concrete);
        };

    }

    /**
     * 找出指定的匿名类，执行bind后的匿名函数
     *
     * @param $abstract
     * @return mixed
     */
    public function make($abstract)
    {
        dump($abstract);
//        if (isset($this->binding[$abstract]['concrete'])) {
            $concrete   = $this->binding[$abstract]['concrete'];
            return $concrete($this);
//        }
//        return null;
    }

    /**
     * 实例化class
     *
     * @param $concrete
     * @return object
     * @throws \Exception
     */
    public function build($concrete)
    {
        try {
            $reflector = new \ReflectionClass($concrete);
            $constructor = $reflector->getConstructor();

            if (is_null($constructor)) {
                return $reflector->newInstance();
            } else {
                $dependencies   = $constructor->getParameters();
//                dump($dependencies);
                $instances = $this->getDependencies($dependencies);
//                dump($instances);
                return $reflector->newInstanceArgs($instances);
            }
        } catch (\ReflectionException $e) {
            throw new \Exception($e->getMessage());
        }
    }

    /**
     * 获取构造方法里面的参数
     *
     * @param $params
     * @return array
     */
    protected function getDependencies($params)
    {
        $dependencies = [];
        foreach ($params as $param) {
            if ($param->getClass() instanceof \ReflectionClass != false) {
                //  又make了一下，将实例化后需要的参数【如果是class的话】进行一个实例化操作
                $dependencies[] = $this->make($param->getClass()->name);
            } else {
//                dump($param->getType());
                switch ($param->getType()) {
                    case 'array':
                        $dependencies[] = (array) ($_GET[$param->name] ?? $param->name);
                        break;
                    case 'float':
                        $dependencies[] = (float) ($_GET[$param->name] ?? $param->name);
                        break;
                    case 'int':
                        $dependencies[] = (int) ($_GET[$param->name] ?? $param->name);
                        break;
                    case 'bool':
                        $dependencies[] = (bool) ($_GET[$param->name] ?? '');
                        break;
                    default:    //  string, null, bool
                        $dependencies[] = (string) $_GET[$param->name] ?? $param->name;
                }
//                $dependencies[] = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : $param->getName();
            }
        }
        return $dependencies;
    }
}